package de.hwrberlin.remo.export.prolog;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;

import de.hwrberlin.remo.export.ModelExporter;
import de.hwrberlin.remo.model.AndRule;
import de.hwrberlin.remo.model.ElementState;
import de.hwrberlin.remo.model.Fact;
import de.hwrberlin.remo.model.KnowledgeBase;
import de.hwrberlin.remo.model.KnowledgeBaseElement;
import de.hwrberlin.remo.model.LinksFromRules;
import de.hwrberlin.remo.model.OrRule;
import de.hwrberlin.remo.model.Rule;

public class PrologExporter implements ModelExporter {

	private final String LINE_BREAK      = "\n";
	
	private final String DEFAULT_VARIABLE_NAME = "X";
	private final String AND_SIGN = ",";
	private final String OR_SIGN  = ";";
	
	@Override
	public void exportKnowledgeBase(String modelFile, KnowledgeBase base) {
		Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
		PrologExportDialog prologExportDialog = new PrologExportDialog(shell);

		// Open export dialog
		if(prologExportDialog.open() == Dialog.OK) {
			
			// Should export knowledge base?
			if(prologExportDialog.isExportKnowledgeBase()) {
				String knowledgeBaseFile = prologExportDialog.getKnowledgeBaseFile();
				exportKnowledgeBase(base, knowledgeBaseFile);
			}
			
			// Should export facts / case?
			if(prologExportDialog.isExportFacts()) {
				String factFile = prologExportDialog.getFactFile();
				String caseName = prologExportDialog.getCaseName().toLowerCase();
				
				exportFacts(base.getFacts(), factFile, caseName);
			}
		}
	}

	private void exportKnowledgeBase(KnowledgeBase base, String outputFile) {
		BufferedWriter writer = getWriter(outputFile);
		if(writer == null) {
			MessageDialog.openError(null, "Export Error", 
					"Can't export knowledge base to file '" + outputFile + "'.");
			return;
		}
		
		try {
			// Write pseudo facts
			for(Fact fact : base.getFacts()) {
				writer.write(getPredicateDeclarationToElement(fact) + LINE_BREAK);
			}
			
			writer.write(LINE_BREAK);
			
			// Write rules
			for(Rule rule : base.getRules()) {
				String ruleSign = getRuleSign(rule);
				
				// Collect premises predicates
				List<String> premisesPredicates = new ArrayList<String>();
				for(LinksFromRules premise : rule.getRelatedlinkedrules()) {
					KnowledgeBaseElement element = premise.getElement();
					premisesPredicates.add(getPredicateToElement(element));
				}
				
				// Concat premises (if existent)
				if(!premisesPredicates.isEmpty()) {
					writer.write(getPredicateToElement(rule) + " :- ");
					writer.write(premisesPredicates.get(0));
					
					for(int i = 1; i < premisesPredicates.size(); i++) {
						writer.write(ruleSign + premisesPredicates.get(i));
					}
					
					writer.write("." + LINE_BREAK);
				}
			}

		} catch (IOException e) {
			e.printStackTrace();

		} finally {
			try {
				writer.flush();
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void exportFacts(EList<Fact> facts, String outputFile, String caseName) {
		BufferedWriter writer = getWriter(outputFile);
		if(writer == null) {
			MessageDialog.openError(null, "Export Error", 
					"Can't export facts to file '" + outputFile + "'.");
			return;
		}
		
		try {
			// Write only true facts to case file
			for(Fact fact : facts) {
				if(fact.getState() == ElementState.TRUE) {
					writer.write(getPredicateToElement(fact, caseName) + ".");
					writer.write(LINE_BREAK);
					
				}
			}

		} catch (IOException e) {
			e.printStackTrace();

		} finally {
			try {
				writer.flush();
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private BufferedWriter getWriter(String fileName) {
		BufferedWriter result = null;

		File outputFile = new File(fileName);
		try {
			result = new BufferedWriter(new FileWriter(outputFile));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return result;
	}
	
	private String getPredicateNameToElement(KnowledgeBaseElement element) {
		String result = element.getName();
		
		if(result == null || result.length() == 0) {
			result = element.toString();
		}
		
		// TODO: String auf Sonderzeichen untersuchen
		
		return result.toLowerCase();
	}
	
	private String getPredicateToElement(KnowledgeBaseElement element) {
		return getPredicateToElement(element, DEFAULT_VARIABLE_NAME);
	}
	
	private String getPredicateToElement(KnowledgeBaseElement element, String variableName) {
		return getPredicateNameToElement(element) + "(" + variableName + ")";
	}
	
	private String getPredicateDeclarationToElement(KnowledgeBaseElement element) {
		return ":- dynamic " + getPredicateNameToElement(element) + "/1.";
	}
	
	private String getRuleSign(Rule rule) {
		if(rule instanceof AndRule) {
			return AND_SIGN;
		
		} else if(rule instanceof OrRule) {
			return OR_SIGN;
		}
		
		return "";
	}
}